﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Media.Imaging;

namespace TriPhoto
{
    public class ImageExifMetadata
    {
        BitmapMetadata _metadata;

        /// <summary>
        /// Constructeur a partir d'une uri
        /// </summary>
        /// <param name="imageUri"></param>
        public ImageExifMetadata(Uri imageUri)
        {
            BitmapFrame frame = BitmapFrame.Create(imageUri, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
            _metadata = (BitmapMetadata)frame.Metadata;

        }

        /// <summary>
        /// constructeur a par d'une BitmapFrame
        /// </summary>
        /// <param name="in_frame"></param>
        public ImageExifMetadata(BitmapFrame in_frame)
        {
            _metadata = (BitmapMetadata)in_frame.Metadata.Clone();
        }

        private decimal ParseUnsignedRational(ulong exifValue)
        {
            return (decimal)(exifValue & 0xFFFFFFFFL) / (decimal)((exifValue & 0xFFFFFFFF00000000L) >> 32);
        }

        private decimal ParseSignedRational(long exifValue)
        {
            return (decimal)(exifValue & 0xFFFFFFFFL) / (decimal)((exifValue & 0x7FFFFFFF00000000L) >> 32);
        }

        /// <summary>
        /// Interogation des metadonnées
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        private object QueryMetadata(string query)
        {
            if (_metadata.ContainsQuery(query))
                return _metadata.GetQuery(query);
            else
                return null;
        }

        /// <summary>
        /// Largeur
        /// </summary>
        public uint? Width
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=40962}");
                if (val == null)
                {
                    return null;
                }
                else
                {
                    if (val.GetType() == typeof(UInt32))
                    {
                        return (uint?)val;
                    }
                    else
                    {
                        return System.Convert.ToUInt32(val);
                    }
                }
            }
        }

        /// <summary>
        /// Hauteur
        /// </summary>
        public uint? Height
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=40963}");
                if (val == null)
                {
                    return null;
                }
                else
                {
                    if (val.GetType() == typeof(UInt32))
                    {
                        return (uint?)val;
                    }
                    else
                    {
                        return System.Convert.ToUInt32(val);
                    }
                }
            }
        }

        /// <summary>
        /// Resolution vertical
        /// </summary>
        public decimal? HorizontalResolution
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=282}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Reslution vertical
        /// </summary>
        public decimal? VerticalResolution
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=283}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Fabirquant de l'appareille
        /// </summary>
        public string EquipmentManufacturer
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=271}");
                return (val != null ? (string)val : String.Empty);
            }
        }

        /// <summary>
        /// Appareille photo
        /// </summary>
        public string CameraModel
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=272}");
                return (val != null ? (string)val : String.Empty);
            }
        }

        /// <summary>
        /// Soft de creation
        /// </summary>
        public string CreationSoftware
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=305}");
                return (val != null ? (string)val : String.Empty);
            }
        }

        /// <summary>
        /// Couleur represention
        /// </summary>
        public ColorRepresentation ColorRepresentation
        {
            get
            {
                if ((ushort)QueryMetadata("/app1/ifd/exif/subifd:{uint=40961}") == 1)
                    return ColorRepresentation.sRGB;
                else
                    return ColorRepresentation.Uncalibrated;
            }
        }

        /// <summary>
        /// Temps d'exposition
        /// </summary>
        public decimal? ExposureTime
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=33434}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Exposition
        /// </summary>
        public decimal? ExposureCompensation
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=37380}");
                if (val != null)
                {
                    return ParseSignedRational((long)val);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Ouverture de la focal
        /// </summary>
        public decimal? LensAperture
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=33437}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Distance focal
        /// </summary>
        public decimal? FocalLength
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=37386}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Vitesse d'obturation
        /// </summary>
        public ushort? IsoSpeed
        {
            get
            {
                return (ushort?)QueryMetadata("/app1/ifd/exif/subifd:{uint=34855}");
            }
        }

        /// <summary>
        /// Flash
        /// </summary>
        public FlashMode FlashMode
        {
            get
            {
                if ((ushort)QueryMetadata("/app1/ifd/exif/subifd:{uint=37385}") % 2 == 1)
                    return FlashMode.FlashFired;
                else
                    return FlashMode.FlashDidNotFire;
            }
        }

        /// <summary>
        /// Mode d'exposition
        /// </summary>
        public ExposureMode ExposureMode
        {
            get
            {
                ushort? mode = (ushort?)QueryMetadata("/app1/ifd/exif/subifd:{uint=34850}");

                if (mode == null)
                {
                    return ExposureMode.Unknown;
                }
                else
                {
                    switch ((int)mode)
                    {
                        case 1: return ExposureMode.Manual;
                        case 2: return ExposureMode.NormalProgram;
                        case 3: return ExposureMode.AperturePriority;
                        case 4: return ExposureMode.ShutterPriority;
                        case 5: return ExposureMode.LowSpeedMode;
                        case 6: return ExposureMode.HighSpeedMode;
                        case 7: return ExposureMode.PortraitMode;
                        case 8: return ExposureMode.LandscapeMode;
                        default: return ExposureMode.Unknown;
                    }
                }
            }
        }

        /// <summary>
        /// Balance des blancs
        /// </summary>
        public WhiteBalanceMode WhiteBalanceMode
        {
            get
            {
                ushort? mode = (ushort?)QueryMetadata("/app1/ifd/exif/subifd:{uint=37384}");

                if (mode == null)
                {
                    return WhiteBalanceMode.Unknown;
                }
                else
                {
                    switch ((int)mode)
                    {
                        case 1: return WhiteBalanceMode.Daylight;
                        case 2: return WhiteBalanceMode.Fluorescent;
                        case 3: return WhiteBalanceMode.Tungsten;
                        case 10: return WhiteBalanceMode.Flash;
                        case 17: return WhiteBalanceMode.StandardLightA;
                        case 18: return WhiteBalanceMode.StandardLightB;
                        case 19: return WhiteBalanceMode.StandardLightC;
                        case 20: return WhiteBalanceMode.D55;
                        case 21: return WhiteBalanceMode.D65;
                        case 22: return WhiteBalanceMode.D75;
                        case 255: return WhiteBalanceMode.Other;
                        default: return WhiteBalanceMode.Unknown;
                    }
                }
            }
        }

        /// <summary>
        /// Date de prise de vue
        /// </summary>
        public DateTime? DateImageTaken
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=36867}");
                if (val == null)
                {
                    return null;
                }
                else
                {
                    string date;
                    if (val is string)
                        date = (string)val;
                    else
                        if (val is string[])
                            date = ((string[])val)[0];
                        else
                            return null;

                    try
                    {
                        string[] l_dateString = date.Split(new char[]{' ',':','/'});
                        try
                        {
                            return new DateTime(
                                int.Parse(l_dateString[0]),    // year
                                int.Parse(l_dateString[1]),    // month
                                int.Parse(l_dateString[2]),    // day
                                int.Parse(l_dateString[3]),   // hour
                                int.Parse(l_dateString[4]),   // minute
                                int.Parse(l_dateString[5])    // second
                            );
                        }
                        catch (Exception ex)
                        {
                            return DateTime.Now;
                        }


                    }
                    catch (FormatException)
                    {
                        return null;
                    }
                    catch (OverflowException)
                    {
                        return null;
                    }
                    catch (ArgumentNullException)
                    {
                        return null;
                    }
                    catch (NullReferenceException)
                    {
                        return null;
                    }
                }
            }

        }
    }

    /// <summary>
    /// Representation couleur
    /// </summary>
    public enum ColorRepresentation
    {
        sRGB,
        Uncalibrated
    }

    /// <summary>
    /// Flash ou pas
    /// </summary>
    public enum FlashMode
    {
        FlashFired,
        FlashDidNotFire
    }

    /// <summary>
    /// Mode d'exposition
    /// </summary>
    public enum ExposureMode
    {
        Manual,
        NormalProgram,
        AperturePriority,
        ShutterPriority,
        LowSpeedMode,
        HighSpeedMode,
        PortraitMode,
        LandscapeMode,
        Unknown
    }

    /// <summary>
    /// Balance des blanc
    /// </summary>
    public enum WhiteBalanceMode
    {
        Daylight,
        Fluorescent,
        Tungsten,
        Flash,
        StandardLightA,
        StandardLightB,
        StandardLightC,
        D55,
        D65,
        D75,
        Other,
        Unknown
    }
}
